package com.ahdy.service;

import com.baidu.aip.ocr.AipOcr;
import jakarta.annotation.PostConstruct;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

@Service
public class BaiduOcrService {
    private static final Logger logger = LoggerFactory.getLogger(BaiduOcrService.class);

    // 百度OCR配置
    @Value("${baidu.ocr.app-id}")
    private String appId;

    @Value("${baidu.ocr.api-key}")
    private String apiKey;

    @Value("${baidu.ocr.secret-key}")
    private String secretKey;

    private AipOcr aipOcr;
    private String cachedAccessToken;
    private long tokenExpireTime;

    @PostConstruct
    public void init() {
        this.aipOcr = new AipOcr(appId, apiKey, secretKey);
        // 设置网络参数
        aipOcr.setConnectionTimeoutInMillis(5000);
        aipOcr.setSocketTimeoutInMillis(10000);
    }

    /**
     * 高精度文字识别（兼容所有SDK版本）
     */
    public String accurateRecognize(MultipartFile file) throws IOException {
        try {
            byte[] imageBytes = validateAndConvertImage(file);

            // 尝试不同版本的方法名
            JSONObject response;
            try {
                // 新版SDK方法
                response = aipOcr.general(imageBytes, getOptions());
            } catch (NoSuchMethodError e1) {
                // 最终回退到HTTP API
                return callByHttpApi(file, true);
            }

            return parseOcrResult(response);
        } catch (Exception e) {
            logger.error("OCR识别失败", e);
            throw new IOException("识别失败: " + e.getMessage());
        }
    }

    /**
     * 高精度文字识别（通过图片路径）
     */
    public String accurateRecognize(String imagePath) throws IOException {
        try {
            byte[] imageBytes = readImageFromPath(imagePath);
            JSONObject response;
            try {
                response = aipOcr.general(imageBytes, getOptions());
            } catch (NoSuchMethodError e1) {
                return callByHttpApi(imageBytes, true);
            }

            return parseOcrResult(response);
        } catch (Exception e) {
            logger.error("OCR识别失败", e);
            throw new IOException("识别失败: " + e.getMessage());
        }
    }

    /**
     * 通用文字识别
     */
    public String basicRecognize(MultipartFile file) throws IOException {
        try {
            byte[] imageBytes = validateAndConvertImage(file);
            JSONObject response = aipOcr.basicGeneral(imageBytes, getOptions());
            return parseOcrResult(response);
        } catch (Exception e) {
            logger.error("OCR识别失败", e);
            throw new IOException("识别失败: " + e.getMessage());
        }
    }

    /**
     * 通用文字识别（通过图片路径）
     */
    public String basicRecognize(String imagePath) throws IOException {
        try {
            byte[] imageBytes = readImageFromPath(imagePath);
            JSONObject response = aipOcr.basicGeneral(imageBytes, getOptions());
            return parseOcrResult(response);
        } catch (Exception e) {
            logger.error("OCR识别失败", e);
            throw new IOException("识别失败: " + e.getMessage());
        }
    }

    /**
     * 从路径读取图片（支持本地路径和网络URL）
     */
    private byte[] readImageFromPath(String imagePath) throws IOException {
        if (imagePath == null || imagePath.trim().isEmpty()) {
            throw new IOException("图片路径不能为空");
        }

        byte[] imageBytes;

        // 判断是否为网络URL
        if (imagePath.toLowerCase().startsWith("http://") ||
                imagePath.toLowerCase().startsWith("https://")) {
            // 从网络URL读取
            imageBytes = readImageFromUrl(imagePath);
        } else {
            // 从本地文件路径读取
            imageBytes = readImageFromLocalPath(imagePath);
        }

        // 验证图片大小
        if (imageBytes.length > 4 * 1024 * 1024) {
            throw new IOException("图片大小不能超过4MB");
        }

        return convertToJpg(imageBytes, null);
    }

    /**
     * 从网络URL读取图片
     */
    private byte[] readImageFromUrl(String imageUrl) throws IOException {
        try {
            URL url = new URL(imageUrl);
            try (InputStream is = url.openStream();
                 ByteArrayOutputStream os = new ByteArrayOutputStream()) {

                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                return os.toByteArray();
            }
        } catch (Exception e) {
            logger.error("从URL读取图片失败: {}", imageUrl, e);
            throw new IOException("无法从URL读取图片: " + e.getMessage());
        }
    }

    /**
     * 从本地路径读取图片
     */
    private byte[] readImageFromLocalPath(String imagePath) throws IOException {
        try {
            Path path = Paths.get(imagePath);
            if (!Files.exists(path)) {
                throw new IOException("图片文件不存在: " + imagePath);
            }

            if (!Files.isReadable(path)) {
                throw new IOException("图片文件无法读取: " + imagePath);
            }

            return Files.readAllBytes(path);
        } catch (Exception e) {
            logger.error("从本地路径读取图片失败: {}", imagePath, e);
            throw new IOException("无法读取本地图片: " + e.getMessage());
        }
    }

    // 以下是辅助方法
    private byte[] validateAndConvertImage(MultipartFile file) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new IOException("请上传有效的图片文件");
        }

        if (file.getSize() > 4 * 1024 * 1024) {
            throw new IOException("图片大小不能超过4MB");
        }

        return convertToJpg(file.getBytes(), file.getContentType());
    }

    private byte[] convertToJpg(byte[] originalBytes, String contentType) throws IOException {
        if (contentType != null &&
                (contentType.equalsIgnoreCase("image/jpeg") ||
                        contentType.equalsIgnoreCase("image/png") ||
                        contentType.equalsIgnoreCase("image/bmp"))) {
            return originalBytes;
        }

        try (InputStream is = new ByteArrayInputStream(originalBytes);
             ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            BufferedImage image = ImageIO.read(is);
            if (image == null) {
                throw new IOException("无法识别的图片格式");
            }
            ImageIO.write(image, "jpg", os);
            return os.toByteArray();
        }
    }

    private HashMap<String, String> getOptions() {
        HashMap<String, String> options = new HashMap<>();
        options.put("language_type", "CHN_ENG");
        options.put("detect_direction", "true");
        options.put("probability", "false");
        return options;
    }

    private String parseOcrResult(JSONObject response) {
        if (response.has("error_code")) {
            throw new RuntimeException(response.optString("error_msg", "OCR识别错误"));
        }

        StringBuilder result = new StringBuilder();
        response.getJSONArray("words_result").forEach(item -> {
            JSONObject word = (JSONObject) item;
            result.append(word.getString("words")).append("\n");
        });
        return result.toString();
    }

    /**
     * HTTP API备用方案（MultipartFile版本）
     */
    private String callByHttpApi(MultipartFile file, boolean highAccuracy) throws IOException {
        String accessToken = getAccessToken();
        String url = highAccuracy ?
                "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic" :
                "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic";

        String imageBase64 = Base64.getEncoder().encodeToString(validateAndConvertImage(file));
        String requestBody = "image=" + URLEncoder.encode(imageBase64, StandardCharsets.UTF_8);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        HttpEntity<String> request = new HttpEntity<>(requestBody, headers);
        ResponseEntity<String> response = new RestTemplate().postForEntity(
                url + "?access_token=" + accessToken, request, String.class);

        return parseOcrResult(new JSONObject(response.getBody()));
    }

    /**
     * HTTP API备用方案（字节数组版本）
     */
    private String callByHttpApi(byte[] imageBytes, boolean highAccuracy) throws IOException {
        String accessToken = getAccessToken();
        String url = highAccuracy ?
                "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic" :
                "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic";

        String imageBase64 = Base64.getEncoder().encodeToString(imageBytes);
        String requestBody = "image=" + URLEncoder.encode(imageBase64, StandardCharsets.UTF_8);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        HttpEntity<String> request = new HttpEntity<>(requestBody, headers);
        ResponseEntity<String> response = new RestTemplate().postForEntity(
                url + "?access_token=" + accessToken, request, String.class);

        return parseOcrResult(new JSONObject(response.getBody()));
    }

    private synchronized String getAccessToken() {
        if (cachedAccessToken != null && System.currentTimeMillis() < tokenExpireTime) {
            return cachedAccessToken;
        }

        try {
            String authUrl = String.format(
                    "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s",
                    apiKey, secretKey);

            String response = new RestTemplate().getForObject(authUrl, String.class);
            JSONObject json = new JSONObject(response);

            cachedAccessToken = json.getString("access_token");
            tokenExpireTime = System.currentTimeMillis() +
                    TimeUnit.SECONDS.toMillis(json.optLong("expires_in", 2592000) - 600);

            return cachedAccessToken;
        } catch (Exception e) {
            throw new RuntimeException("获取Access Token失败", e);
        }
    }
}
